﻿/******************************************************************************
* SunnyUI.FrameDecoder 开源TCP、串口数据解码库。
* CopyRight (C) 2022-2023 ShenYongHua(沈永华).
* QQ群：56829229 QQ：17612584 EMail：SunnyUI@qq.com
*
* Blog:   https://www.cnblogs.com/yhuse
* Gitee:  https://gitee.com/yhuse/SunnyUI.FrameDecoder
*
* SunnyUI.FrameDecoder.dll can be used for free under the MIT license.
* If you use this code, please keep this note.
* 如果您使用此代码，请保留此说明。
******************************************************************************
* 文件名称: DecoderHelper.cs
* 文件说明: 数据帧解码器帮助类
* 当前版本: V1.0
* 创建日期: 2022-11-01
*
* 2022-11-01: V1.0.0 增加文件说明
******************************************************************************/

using System;
using System.Text;

namespace Sunny.FrameDecoder
{
    internal static class ConstDefine
    {
        public const int DefaultNetBufferSize = 64 * 1024;

        public const int CacheSize = 1024;
    }

    /// <summary>
    /// 数据帧解码器帮助类
    /// </summary>
    public static class DecoderHelper
    {
        /// <summary>
        /// 数据长度类型的长度
        /// </summary>
        /// <param name="valueLengthType">数据长度类型</param>
        /// <returns>长度</returns>
        public static int Length(this ValueLengthType valueLengthType)
        {
            switch (valueLengthType)
            {
                case ValueLengthType.Byte:
                    return 1;
                case ValueLengthType.UShort:
                    return 2;
                case ValueLengthType.UInt:
                    return 4;
                default:
                    return 1;
            }
        }

        internal static int GetLengthValue(this ReadOnlySpan<byte> data, int offset, int length, bool isLittleEndian = true)
        {
            if (length == 1)
            {
                return data[offset];
            }

            if (length == 2)
            {
                return ByteHelper.ToUShort(data, offset, isLittleEndian);
            }

            if (length == 4)
            {
                return ByteHelper.ToInt(data, offset, isLittleEndian);
            }

            return -1;
        }

        /// <summary>
        /// 得到最接近数字且大于数字的二的N次方数
        /// </summary>
        /// <param name="value">数字</param>
        /// <returns>二的N次方数</returns>
        public static int Get2PowNHigh(int value)
        {
            --value;//避免正好输入一个2的次方数
            value |= value >> 1;
            value |= value >> 2;
            value |= value >> 4;
            value |= value >> 8;
            value |= value >> 16;
            return ++value;
        }

        /// <summary>
        /// 得到最接近数字且小于数字的二的N次方数
        /// </summary>
        /// <param name="value">数字</param>
        /// <returns>二的N次方数</returns>
        public static int Get2PowNLow(int value)
        {
            return Get2PowNHigh(value) >> 1;
        }

        internal static int GetLengthValue(this Span<byte> span, bool isLittleEndian = true)
        {
            if (span == null)
            {
                return -1;
            }

            if (span.Length == 1)
            {
                return span[0];
            }

            if (span.Length == 2)
            {
                ByteHelper.ToUShort(span, 0, isLittleEndian);
            }

            if (span.Length == 4)
            {
                ByteHelper.ToInt(span, 0, isLittleEndian);
            }

            return -1;
        }
    }

    internal static class EmptyArray<T>
    {
        public static readonly T[] Value = new T[0];
    }

    internal static class StringBuilderCache
    {
        // The value 360 was chosen in discussion with performance experts as a compromise between using
        // as litle memory (per thread) as possible and still covering a large part of short-lived
        // StringBuilder creations on the startup path of VS designers.
        private const int MAX_BUILDER_SIZE = 360;

        [ThreadStatic]
        private static StringBuilder CachedInstance;

        internal const int DefaultCapacity = 16;

        public static StringBuilder Acquire(int capacity = DefaultCapacity)
        {
            if (capacity <= MAX_BUILDER_SIZE)
            {
                StringBuilder sb = StringBuilderCache.CachedInstance;
                if (sb != null)
                {
                    // Avoid stringbuilder block fragmentation by getting a new StringBuilder
                    // when the requested size is larger than the current capacity
                    if (capacity <= sb.Capacity)
                    {
                        StringBuilderCache.CachedInstance = null;
                        sb.Clear();
                        return sb;
                    }
                }
            }
            return new StringBuilder(capacity);
        }

        public static void Release(StringBuilder sb)
        {
            if (sb.Capacity <= MAX_BUILDER_SIZE)
            {
                StringBuilderCache.CachedInstance = sb;
            }
        }

        public static string GetStringAndRelease(StringBuilder sb)
        {
            string result = sb.ToString();
            Release(sb);
            return result;
        }
    }
}
